#include "pch.h"
#include "Effect.h"
namespace mini
{
	CEffect::CEffect()
	{
		mType = Effect;
	}

	CEffect::~CEffect()
	{
		SafeRelease(mpMaterialUnit);
		SafeRelease(mpShader);
		clearAllActors();
	}

	bool CEffect::init(CDevice* pDevice)
	{
		mpDevice = pDevice;
		return false;
	}

	void CEffect::bindMaterial(const std::string& szName)
	{
		CMaterialUnit* pMaterialUnit = new CMaterialUnit;
		pMaterialUnit->setUnitFile(szName);
		pMaterialUnit->onInitialise();
		mpMaterialUnit = pMaterialUnit;
		mpMaterialUnit->setProjectMatrixForAllShaderUnit(GEngine->getProjectMatrix());
		mpMaterialUnit->setViewMatrixForAllShaderUnit(GEngine->getViewMatrix());
	}

	void CEffect::commitToGpu(DeviceContext* pDeviceContext)
	{
		CShaderUnit* pShaderUnit = nullptr;
		auto _render = [&](std::map<CShaderUnit*, std::list<CMeshUnit*>>& renderUnit )
		{
			for (auto& item : renderUnit)
			{
				for (auto& child : (item.second))
				{
					pShaderUnit->setShader(pDeviceContext);
					pShaderUnit->setWorldMatrix(&child->getMatrix());
					pShaderUnit->setViewMatrix(GEngine->getViewMatrix());
					pShaderUnit->setProjectMatrix(GEngine->getProjectMatrix());
					pShaderUnit->apply(pDeviceContext);
					mpMaterialUnit->apply(pDeviceContext);
					child->render(pDeviceContext);
				}
			}
		};
		int nPassCount = mpMaterialUnit->getShaderUnitSize();
		for (int i = 0; i < nPassCount; ++i)
		{
			pShaderUnit = mpMaterialUnit->getShaderUnit(i);
			if (nullptr != pShaderUnit)
			{
				for (auto& item : mZbufferOffRenderUnits)
				{
					auto _item = (item.second);
					(_item.second)->render(pDeviceContext);
				}
				_render(mRenderUnits);
				_render(mAlphaRenderUnits);
			}
		}
		cleanUnit();
	}

	void CEffect::addActor(CActor* pActor)
	{
		if (nullptr != pActor)
		{
			auto item = std::find(mActors.begin(), mActors.end(), pActor);
			if (item == mActors.end())
			{
				mActors.push_back(pActor);
			}
		}
	}

	void CEffect::removeActor(CActor* pActor)
	{
		if (nullptr != pActor)
		{
			auto item = std::find(mActors.begin(), mActors.end(), pActor);
			if (item != mActors.end())
			{
				mActors.erase(item);
			}
		}
	}

	void CEffect::clearAllActors()
	{
		mActors.clear();
	}

	void CEffect::addUnit(CUnit* pUnit, EntityType Type, INT32 zLayer)
	{
		CMeshUnit* pMeshUnit = dynamic_cast<CMeshUnit*>(pUnit);
		if (pMeshUnit)
		{
			CMaterialUnit* pMeshMaterilaUnit = pMeshUnit->getMaterialUnit();
			if (pMeshMaterilaUnit != nullptr)
			{
				CShaderUnit* pShaderUnit = pMeshMaterilaUnit->getShaderUnit(0);
				if (pShaderUnit != nullptr)
				{
					if (pMeshMaterilaUnit->isZbufferWrite())
					{
						if (pMeshMaterilaUnit->isAlpha())
						{
							mAlphaRenderUnits[pShaderUnit].push_back(pMeshUnit);
						}
						else
						{
							mRenderUnits[pShaderUnit].push_back(pMeshUnit);
						}
					}
					else
					{
						mZbufferOffRenderUnits[zLayer] = std::make_pair(pShaderUnit, pMeshUnit);
					}
				}
			}
		}
	}

	void CEffect::cleanUnit()
	{
		mZbufferOffRenderUnits.clear();
		mRenderUnits.clear();
		mAlphaRenderUnits.clear();
	}

	EffectOrder CEffect::getOrder()
	{
		return mOrder;
	}

}